package org.rain.common.util.storage.support;

import lombok.NonNull;
import org.rain.common.exception.BaseException;
import org.rain.common.util.storage.IFileStorageService;
import org.rain.common.util.Strs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * 以本地磁盘作为文件持久化存储介质
 * Created by yt on 2024/4/9 21:43
 */
public class LocalFileStorageServiceImpl implements IFileStorageService {

    private static final Logger log = LoggerFactory.getLogger(LocalFileStorageServiceImpl.class);
    private final String FILE_UPLOAD_PATH;

    public LocalFileStorageServiceImpl(String uploadPath) {
        if (Strs.isEmpty(uploadPath)) {
            uploadPath = "file-storage/";
        }
        if (!uploadPath.endsWith("/")) {
            uploadPath = uploadPath + "/";
        }
        FILE_UPLOAD_PATH = uploadPath;
        log.info("当前的文件存储类：{}", this.getClass().getName());
        log.info("文件会被上传到：{}", FILE_UPLOAD_PATH);
    }

    /**
     * 上传文件
     */
    @Override
    public String upload(InputStream is) {
        //使用当前时间戳的36进制+6位随机字符串作为文件id
        String fileId = Long.toString(System.currentTimeMillis(), Character.MAX_RADIX).toUpperCase() + "-" + Strs.randomStr(6, 0, 61);
        Path path = Path.of(FILE_UPLOAD_PATH, fileId);

        try {
            //如果文件夹已经存在，这种情况几乎不可能，为了严谨，还是考虑这种极端情况
            if (Files.exists(path) && Files.isDirectory(path)) {
                String newFileId;
                do {
                    newFileId = fileId + "," + System.currentTimeMillis();
                } while (Files.exists(path.resolve(newFileId)));
                fileId = newFileId;
            }
            //如果文件夹不存在，正常情况
            else {
                //创建文件夹
                Files.createDirectories(path);
            }
            //创建文件并写入数据
            Files.copy(is, path.resolve(fileId));
        } catch (IOException e) {
            throw new BaseException(e);
        }
        log.info("文件上传成功，fileId: {}", fileId);
        return fileId;
    }

    /**
     * 根据fileId获取文件流
     */
    @Override
    public InputStream download(@NonNull String fileId) {
        Path path = exist(fileId);
        //判断文件是否存在
        if (path == null) {
            throw new BaseException("文件不存在: " + fileId);
        }

        try {
            return Files.newInputStream(path);
        } catch (IOException e) {
            throw new BaseException(e);
        }
    }

    /**
     * 根据fileId删除文件
     */
    @Override
    public Boolean remove(@NonNull String fileId) {
        Path file = exist(fileId);
        if (file != null) {
            try {
                boolean isSuccess = Files.deleteIfExists(file);
                try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(file.getParent())) {
                    //如果父文件是空文件，就删除
                    if (!dirStream.iterator().hasNext()) {
                        Files.delete(file.getParent());
                    }
                } catch (Exception e) {
                    log.error("删除父目录 [{}] 失败", file.getParent(), e);
                }
                return isSuccess;
            } catch (IOException e) {
                throw new BaseException(e);
            }
        }
        return false;
    }

    /**
     * 根据fileId判断文件是否存在
     */
    @Override
    public Path exist(@NonNull String fileId) {
        Path path = getPathByFileId(fileId);
        if (Files.exists(path) && Files.isRegularFile(path)) {
            return path;
        }
        return null;
    }

    /**
     * 根据fileId获取文件路径
     */
    private Path getPathByFileId(String fileId) {
        //获取文件路径
        Path path;
        if (fileId.contains(",")) {
            String[] fileIdInfos = fileId.split(",", 2);
            if (fileIdInfos.length != 2) {
                throw new BaseException("文件ID格式错误: " + fileId);
            }
            path = Path.of(FILE_UPLOAD_PATH, fileIdInfos[0], fileId);
        } else {
            path = Path.of(FILE_UPLOAD_PATH, fileId, fileId);
        }
        return path;
    }

}
